﻿' =====================================================================
'  File:		DynamicEntityPartialType.cs
'  Summary:	Defines concrete properties of DynamicEntity.
' =====================================================================
'  CONVERTED TO VB.NET!!
'
'  This file is part of the Microsoft CRM V4 SDK Code Samples.
'
'  Copyright (C) 2006 Microsoft Corporation.  All rights reserved.
'
'  This source code is intended only as a supplement to Microsoft
'  Development Tools and/or on-line documentation.  See these other
'  materials for detailed information regarding Microsoft code samples.
'
'  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
'  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
'  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
'  PARTICULAR PURPOSE.
'
' =====================================================================
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Reflection

Namespace CRM

    Partial Public Class DynamicEntity
        Public Sub Remove(ByVal propertyName As String)
            Me.Indexer.RemoveProperty(propertyName)
        End Sub

        Public Function Contains(ByVal propertyName As String) As Boolean
            Dim [property] As [Property]
            Return Me.Indexer.TryGetProperty(propertyName, [property])
        End Function

        Default Public Property Item(ByVal propertyName As String) As Object
            Get
                Dim [property] As [Property]
                If Indexer.TryGetProperty(propertyName, [property]) Then
                    Return PropertyFactory.GetPropertyValue([property])
                End If

                Throw New ArgumentException(String.Format("Property '{0}' is not found.", propertyName), "propertyName")
            End Get

            Set(ByVal value As Object)
                Dim [property] As [Property]
                If Indexer.TryGetProperty(propertyName, [property]) Then
                    PropertyFactory.SetPropertyValue([property], value)
                Else
                    Indexer.AddNewProperty(propertyName, value)
                End If
            End Set
        End Property

        Public Function GetValue(ByVal propertyName As String) As String

            Dim [property] As [Property]
            Dim objValue As Object

            If Indexer.TryGetProperty(propertyName, [property]) Then
                objValue = PropertyFactory.GetPropertyValue([property])
            Else
                Return ""
            End If

            If TypeOf objValue Is CrmBoolean Then
                Return DirectCast(objValue, CrmBoolean).Value.ToString
            ElseIf TypeOf objValue Is CrmDateTime Then
                Return DirectCast(objValue, CrmDateTime).ToString
            ElseIf TypeOf objValue Is CrmDecimal Then
                Return DirectCast(objValue, CrmDecimal).formattedvalue
            ElseIf TypeOf objValue Is CrmFloat Then
                Return DirectCast(objValue, CrmFloat).formattedvalue
            ElseIf TypeOf objValue Is CrmMoney Then
                Return DirectCast(objValue, CrmMoney).formattedvalue
            ElseIf TypeOf objValue Is CrmNumber Then
                Return DirectCast(objValue, CrmNumber).formattedvalue
            ElseIf TypeOf objValue Is Customer Then
                Return DirectCast(objValue, Customer).name
            ElseIf TypeOf objValue Is DynamicEntity() Then
                Return "[DynamicEntity]"
            ElseIf TypeOf objValue Is EntityNameReference Then
                Return "[EntityNameReference]"
            ElseIf TypeOf objValue Is Key Then
                Return DirectCast(objValue, Key).Value.ToString
            ElseIf TypeOf objValue Is Lookup Then
                Return DirectCast(objValue, Lookup).name
            ElseIf TypeOf objValue Is Owner Then
                Return DirectCast(objValue, Owner).name
            ElseIf TypeOf objValue Is Picklist Then
                Return DirectCast(objValue, Picklist).name
            ElseIf TypeOf objValue Is Status Then
                Return DirectCast(objValue, Status).name
            ElseIf TypeOf objValue Is String Then
                Return DirectCast(objValue, String).ToString
            ElseIf TypeOf objValue Is UniqueIdentifier Then
                Return DirectCast(objValue, UniqueIdentifier).Value.ToString
            Else
                Throw New ArgumentException("Property type is not supported.", "value")
            End If

            Return ""

        End Function

        Private ReadOnly Property Indexer() As PropertyIndexer
            Get
                If _propertyIndexer Is Nothing Then
                    _propertyIndexer = New PropertyIndexer(Me)
                End If

                Return _propertyIndexer
            End Get
        End Property

        <NonSerialized()> _
        Private _propertyIndexer As PropertyIndexer

        Private Class PropertyFactory
            Public Shared Function CreateInstance(ByVal propertyName As String, ByVal value As Object) As [Property]
                Dim p As [Property] = Nothing
                If TypeOf value Is CrmBoolean Then
                    p = New CrmBooleanProperty()
                ElseIf TypeOf value Is CrmDateTime Then
                    p = New CrmDateTimeProperty()
                ElseIf TypeOf value Is CrmDecimal Then
                    p = New CrmDecimalProperty()
                ElseIf TypeOf value Is CrmFloat Then
                    p = New CrmFloatProperty()
                ElseIf TypeOf value Is CrmMoney Then
                    p = New CrmMoneyProperty()
                ElseIf TypeOf value Is CrmNumber Then
                    p = New CrmNumberProperty()
                ElseIf TypeOf value Is Customer Then
                    p = New CustomerProperty()
                ElseIf TypeOf value Is DynamicEntity() Then
                    p = New DynamicEntityArrayProperty()
                ElseIf TypeOf value Is EntityNameReference Then
                    p = New EntityNameReferenceProperty()
                ElseIf TypeOf value Is Key Then
                    p = New KeyProperty()
                ElseIf TypeOf value Is Lookup Then
                    p = New LookupProperty()
                ElseIf TypeOf value Is Owner Then
                    p = New OwnerProperty()
                ElseIf TypeOf value Is Picklist Then
                    p = New PicklistProperty()
                ElseIf TypeOf value Is Status Then
                    p = New StatusProperty()
                ElseIf TypeOf value Is String Then
                    If propertyName = "statecode" Then
                        p = New StateProperty()
                    Else
                        p = New StringProperty()
                    End If
                ElseIf TypeOf value Is UniqueIdentifier Then
                    p = New UniqueIdentifierProperty()
                Else
                    Throw New ArgumentException("Property type is not supported.", "value")
                End If

                p.Name = propertyName
                SetPropertyValue(p, value)

                Return p
            End Function

            Public Shared Function GetPropertyValue(ByVal [property] As [Property]) As Object
                Dim p As PropertyInfo = [property].[GetType]().GetProperty("Value")

                Return p.GetValue([property], Nothing)
            End Function

            Public Shared Sub SetPropertyValue(ByVal [property] As [Property], ByVal value As Object)
                Dim p As PropertyInfo = [property].[GetType]().GetProperty("Value")

                p.SetValue([property], value, Nothing)
            End Sub
        End Class

        Private NotInheritable Class PropertyIndexer
            Private _entity As DynamicEntity
            Private _nameToIndex As Dictionary(Of String, Integer)

            Public Sub New(ByVal entity As DynamicEntity)
                _entity = entity
                _nameToIndex = New Dictionary(Of String, Integer)()
            End Sub

            Public Function TryGetProperty(ByVal propertyName As String, ByRef [property] As [Property]) As Boolean
                Dim index As Integer
                If _nameToIndex.TryGetValue(propertyName, index) AndAlso index < _entity.Properties.Length Then
                    [property] = _entity.Properties(index)

                    If [property].Name = propertyName Then
                        Return True
                    End If
                End If

                ReIndex()
                If _nameToIndex.TryGetValue(propertyName, index) Then
                    [property] = _entity.Properties(index)

                    Return True
                End If

                [property] = Nothing
                Return False
            End Function

            Public Function AddNewProperty(ByVal propertyName As String, ByVal value As Object) As [Property]
                Dim [property] As [Property] = PropertyFactory.CreateInstance(propertyName, value)

                If _entity.Properties Is Nothing Then
                    _entity.Properties = New [Property](1) {}
                Else
                    Array.Resize(Of [Property])(_entity.propertiesField, _entity.Properties.Length + 1)
                End If

                _entity.propertiesField(_entity.propertiesField.Length - 1) = [property]

                _nameToIndex.Add(propertyName, _entity.propertiesField.Length - 1)

                Return [property]
            End Function

            Public Sub RemoveProperty(ByVal propertyName As String)
                Dim [property] As [Property]
                Dim index As Integer
                If _nameToIndex.TryGetValue(propertyName, index) Then
                    If index < _entity.Properties.Length Then
                        [property] = _entity.Properties(index)

                        If [property].Name = propertyName Then
                            Me.RemovePropertyAt(index)
                            Return
                        End If
                    End If
                End If

                ReIndex()
                If _nameToIndex.TryGetValue(propertyName, index) Then
                    Me.RemovePropertyAt(index)
                End If
            End Sub

            Private Sub RemovePropertyAt(ByVal index As Integer)
                ' Move the last element into the index of the item removed.
                If index <> _entity.Properties.Length - 1 Then
                    _entity.Properties(index) = _entity.Properties(_entity.Properties.Length - 1)
                End If

                ' Resize the array one smaller.
                Array.Resize(Of [Property])(_entity.propertiesField, _entity.Properties.Length - 1)
            End Sub

            Private Sub ReIndex()
                If _entity.propertiesField IsNot Nothing Then
                    _nameToIndex = New Dictionary(Of String, Integer)(_entity.propertiesField.Length)

                    Dim i As Integer = 0
                    While i < _entity.propertiesField.Length
                        Dim [property] As [Property] = _entity.propertiesField(i)

                        _nameToIndex.Add([property].Name, i)
                        System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
                    End While
                Else
                    _nameToIndex = New Dictionary(Of String, Integer)()
                End If
            End Sub

        End Class
    End Class
End Namespace
